//
//  NullSafe.m
//
//  Version 2.0
//
//  Created by 海参 on 02/11/2022.
//  Copyright © 2022 com.Chs. All rights reserved.
//
//  简介：遇到NSNull对象崩溃，会有如下信息-[NSNull objectForKey:]: unrecognized selector sent to instance 0x7fff8a6d5fa0。使用本工具处理之后，<null>对象对未识别的消息会返回nil，不会崩溃。简化NullSafe，只处理JSON和Plist值。
//  发送消息对象改为nil后，<null>对象的调用返回值等于nil。注意：0(<nil>) = nil；


///注意：framework的预编译宏比工程业务的预编译宏执行的早，所以不适合用
//#ifndef NULLSAFE_ENABLED
//#define NULLSAFE_ENABLED 1
//#endif

///默认不启用
NSInteger nullSafeSDKEnable = 0;


@implementation NSNull (CAINullSafe)


///如果对象维护委托或能够处理它没有直接实现的消息，则应该重写此方法以返回适当的methodSignature(方法签名)。转发消息的机制使用从methodSignatureForSelector:获得的信息来创建要转发的NSInvocation对象。
///要响应对象本身不能识别的方法，必须重写forwardInvocation:之外的methodSignatureForSelector:。返回一个方法签名对象，它包含由aSelector识别的实例方法的描述,因为默认找不到方法才进来的，所以肯定为nil
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
    if (nullSafeSDKEnable)
    {
        for (Class someClass in @[
            [NSMutableArray class],
            [NSMutableDictionary class],
            [NSMutableString class],
            [NSNumber class],
            [NSDate class],
            [NSData class]
        ])
        {
            if ([someClass instancesRespondToSelector:selector])
            {
                NSMethodSignature *signature = [someClass instanceMethodSignatureForSelector:selector];
                return signature;
                break;
            }
        }
    }
    NSMethodSignature *signature = [super methodSignatureForSelector:selector];
    return signature;
}


//当向对象发送没有相应方法的消息时，runtime(运行时)系统将给接收方一个机会将消息委托给另一个接收方。它通过创建一个代表消息的NSInvocation对象来委派消息，并向接收者发送一个包含这个NSInvocation对象作为参数的forwardInvocation:消息。然后，接收方的forwardInvocation:方法可以选择将消息转发到另一个对象。(如果那个对象也不能回应消息，它也会有机会转发它。)
- (void)forwardInvocation:(NSInvocation *)invocation
{
    invocation.target = nil;
    [invocation invoke];
}

- (void)doesNotRecognizeSelector:(SEL)aSelector{
    [super doesNotRecognizeSelector:aSelector];
}

/*
static NSMutableSet<Class> *classList = nil;
static NSMutableDictionary<NSString *, id> *signatureCache = nil;
static void cacheSignatures()
{
    classList = [[NSMutableSet alloc] init];
    signatureCache = [[NSMutableDictionary alloc] init];

    //get class list
    int numClasses = objc_getClassList(NULL, 0);
    Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses);
    numClasses = objc_getClassList(classes, numClasses);

    //add to list for checking
    for (int i = 0; i < numClasses; i++)
    {
        //determine if class has a superclass
        Class someClass = classes[i];
        Class superclass = class_getSuperclass(someClass);
        while (superclass)
        {
            if (superclass == [NSObject class])
            {
                [classList addObject:someClass];
                [classList removeObject:[someClass superclass]];
                break;
            }
            superclass = class_getSuperclass(superclass);
        }
    }

    //free class list
    free(classes);
}
*/

@end


